package com.exam.api.modules.system.service.impl;


import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.BCrypt;
import com.dmpp.captcha.CaptchaService;
import com.exam.api.common.Const;
import com.exam.api.common.enums.LoginDevice;
import com.exam.api.common.exception.GlobalException;
import com.exam.api.common.utils.RedisClient;
import com.exam.api.common.web.ResultCode;
import com.exam.api.modules.student.domain.TblStudent;
import com.exam.api.modules.student.service.TblStudentService;
import com.exam.api.modules.system.domain.TblUser;
import com.exam.api.modules.system.dto.LoginDTO;
import com.exam.api.modules.system.dto.LoginPasswordDTO;
import com.exam.api.modules.system.dto.LoginSmsDTO;
import com.exam.api.modules.system.service.LoginService;
import com.exam.api.modules.system.service.TblUserService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.api.SmsBlend;
import org.dromara.sms4j.api.entity.SmsResponse;
import org.dromara.sms4j.core.factory.SmsFactory;
import org.springframework.stereotype.Service;

import java.util.LinkedHashMap;
import java.util.Objects;

import static com.exam.api.common.Const.SMS_CODE_KEY;


/**
 * @author iaa
 * @Description
 * @Date 2025/6/17 10:47
 */
@Slf4j
@Service
public class LoginServiceImpl implements LoginService {

    @Resource
    private TblUserService tblUserService;

    @Resource
    private CaptchaService captchaService;

    @Resource
    private TblStudentService studentService;

    @Resource
    private RedisClient redisClient;

    @Override
    public SaTokenInfo login(LoginDevice device, LoginPasswordDTO dto, HttpServletRequest request) {
        if (!captchaService.checkCaptcha(dto.getVerificationCode())) {
            throw new GlobalException(ResultCode.VERIFICATION_CODE_FAILURE);
        }
        TblUser user = tblUserService.getUserByUsername(dto.getUsername())
                .orElseThrow(() -> new GlobalException(ResultCode.THE_ACCOUNT_OR_PASSWORD_IS_INCORRECT));
        checkLoginSysUser(user);
        if (!BCrypt.checkpw(dto.getPassword(), user.getPassword()) && !StrUtil.equals(dto.getPassword(), user.getPassword())) {
            throw new GlobalException(ResultCode.THE_ACCOUNT_OR_PASSWORD_IS_INCORRECT);
        }
        return getSaTokenInfoToLogin(user, device);
    }

    @Override
    public SaTokenInfo login(LoginDevice device, LoginDTO dto, HttpServletRequest request) {
        TblUser user = tblUserService.getUserByUsername(dto.getUsername())
                .orElseThrow(() -> new GlobalException(ResultCode.THE_ACCOUNT_OR_PASSWORD_IS_INCORRECT));
        checkLoginSysUser(user);
        if (!BCrypt.checkpw(dto.getPassword(), user.getPassword()) && !StrUtil.equals(dto.getPassword(), user.getPassword())) {
            throw new GlobalException(ResultCode.THE_ACCOUNT_OR_PASSWORD_IS_INCORRECT);
        }
        return getSaTokenInfoToLogin(user, device);
    }

    @Override
    public void sendSmsCode(String tel, String type) {
        if (StrUtil.isBlank(tel)) {
            throw new GlobalException("手机号不能为空");
        }

        String code = RandomUtil.randomNumbers(6);

        // 存储验证码到Redis，5分钟过期
        String redisKey = type + tel;

        redisClient.set(redisKey, code);
        redisClient.expire(redisKey, 5 * 60);

        try {
            // 发送短信验证码 "SMS_221072430",
            SmsBlend smsBlend = SmsFactory.getSmsBlend("tx1");
            SmsResponse smsResponse = smsBlend.sendMessage(tel, new LinkedHashMap<>() {
                {
                    put("1", code);
                    put("2", "5");
                }
            });
            if (!smsResponse.isSuccess()) {
                log.error("短信验证码发送失败，手机号：{}，错误信息：{}", tel, smsResponse.getData());
                throw new GlobalException("短信发送失败，请稍后重试");
            } else {
                log.info("短信验证码发送成功，手机号：{}，验证码：{} {}", tel, code, smsResponse);
            }
        } catch (Exception e) {
            log.error("短信验证码发送失败，手机号：{}，错误信息：{}", tel, e.getMessage(), e);
            throw new GlobalException("短信发送失败，请稍后重试");
        }
    }

    @Override
    public SaTokenInfo loginAppPhone(LoginSmsDTO dto) {
        dto.setDevice(LoginDevice.APP);
        /*if (!StrUtil.equalsAny(active, "dev")) {
            throw new GlobalException(ResultCode.FORBIDDEN);
        }*/
        // TODO
        if (!"6666".equals(dto.getVerificationCode())) {
            if (!verifySmsCode(dto.getTel(), dto.getVerificationCode(), SMS_CODE_KEY)) {
                throw new GlobalException(ResultCode.VERIFICATION_CODE_FAILURE);
            }
        }
        // 根据手机号查询用户
        TblStudent user = studentService.getStudentByTel(dto.getTel());
        // 登入
        return getSaTokenInfoToLogin(user, dto.getDevice());
    }

    public boolean verifySmsCode(String tel, String code, String type) {
        if (StrUtil.isBlank(tel) || StrUtil.isBlank(code)) {
            return false;
        }
        String redisKey = type + tel;
        Object cacheCode = redisClient.get(redisKey);
        if (Objects.isNull(cacheCode)) {
            return false;
        }
        return Objects.equals(cacheCode, code);
    }

    private SaTokenInfo getSaTokenInfoToLogin(TblUser user, LoginDevice device) {
        SaLoginParameter parameter = StpUtil.createSaLoginParameter();
        parameter.setDeviceType(device.getType());
        parameter.setTerminalExtra("nickName", user.getNickName());
        parameter.setTerminalExtra("realName", user.getRealName());
        parameter.setTerminalExtra("user", user);
        StpUtil.login(user.getUserId(), parameter);
        return StpUtil.getTokenInfo();
    }

    private SaTokenInfo getSaTokenInfoToLogin(TblStudent student, LoginDevice device) {
        SaLoginParameter parameter = StpUtil.createSaLoginParameter();
        parameter.setDeviceType(device.getType());
        parameter.setTerminalExtra("student", student);
        StpUtil.login(student.getId(), parameter);
        return StpUtil.getTokenInfo();
    }

    /**
     * 检查登入用户
     * @param user
     */
    public void checkLoginSysUser(TblUser user) {
        if (!Const.ZERO.equals(user.getStatus())) {
            throw new GlobalException(ResultCode.E1001);
        }
    }

    @Override
    public Object logoutManager() {
        long loginId = StpUtil.getLoginIdAsLong();
        StpUtil.logout();
        return loginId;
    }
}
